gusucode.com > VC++ 基于IE内核功能很齐全的浏览器(支持多标签)-源码程序 > VC++ 基于IE内核功能很齐全的浏览器(支持多标签)-源码程序/code/Explorer/CJFlatTabCtrl.cpp

    // FlatTab.cpp : implementation file
//
// Copyright ? 1999 Ian Brumby
//
// This source code may be used in compiled form in any way you desire. 
// Source file(s) may be redistributed unmodified by any means PROVIDING
// they are not sold for profit without the authors expressed written consent,
// and providing that this notice and the authors name and all copyright
// notices remain intact.
//
// ==========================================================================  
// HISTORY:	  
// ==========================================================================  
//			1.4 	21 Jul 1999	- Initial release.
//			1.5		30 Aug 1999 - Several enhancements by Phil Baxter 
//					<Phil.Baxter@mrjinx.demon.co.uk>
// ==========================================================================  

// Excel uses FF_ROMAN and Font Height = ~(control's height - 4)
// Excel draws its tabs right to left
// Excel has 3DLIGHT highlighting on the left diagonal
// Excel has home/end buttons
// Excel's buttons change width as the control's height changes
//
/////////////////////////////////////////////////////////////////////////////
/****************************************************************************
 *
 * $Date: 10/26/99 10:52p $
 * $Revision: 11 $
 * $Archive: /CodeJock/CJLibrary/CJFlatTabCtrl.cpp $
 *
 * $History: CJFlatTabCtrl.cpp $
 * 
 * *****************  Version 11  *****************
 * User: Kirk Stowell Date: 10/26/99   Time: 10:52p
 * Updated in $/CodeJock/CJLibrary
 * Fixed bug with flat tab where in some cases tabs were not completely
 * painted.
 * 
 * *****************  Version 10  *****************
 * User: Kirk Stowell Date: 10/25/99   Time: 10:52p
 * Updated in $/CodeJock/CJLibrary
 * Modified resource include for static builds.
 * 
 * *****************  Version 9  *****************
 * User: Kirk Stowell Date: 10/24/99   Time: 12:15a
 * Updated in $/CodeJock/CJLibrary
 * Fixed minor bug with tab font creation.
 * 
 * *****************  Version 8  *****************
 * User: Kirk Stowell Date: 10/24/99   Time: 12:01a
 * Updated in $/CodeJock/CJLibrary
 * Fixed potential resource and memory leak problems.
 * 
 * *****************  Version 7  *****************
 * User: Kirk Stowell Date: 10/14/99   Time: 11:37p
 * Updated in $/CodeJock/CJLibrary
 * Fixed bug with flat tab control that would have kept one from disabling
 * the l/r arrow buttons, which caused an exception to be thrown. Thanks
 * to Ted Crow [crowtc@surge.net] for help with this.
 * 
 * *****************  Version 6  *****************
 * User: Kirk Stowell Date: 10/14/99   Time: 12:25p
 * Updated in $/CodeJock/CJLibrary
 * Added source control history to file header.
 *
 ***************************************************************************/
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CJResource.h"
#include "CJFlatTabCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCJFlatTabCtrl

CCJFlatTabCtrl::CCJFlatTabCtrl()
{
	m_bHasArrows          = false;
	m_bHasHomeEnd         = false;
	m_bTabsOnBottom       = false;
	m_iCurSel             = -1;
	m_pLeftButton         = NULL;
	m_pRightButton        = NULL;
	m_pHomeButton         = NULL;
	m_pEndButton          = NULL;
	m_hLeftArrow          = NULL;
	m_hLeftArrowDisabled  = NULL;
	m_hRightArrow         = NULL;
	m_hRightArrowDisabled = NULL;
	m_hHomeArrow          = NULL;
	m_hHomeArrowDisabled  = NULL;
	m_hEndArrow           = NULL;
	m_hEndArrowDisabled   = NULL;
	m_iXOffset            = 0;
	m_nMaxTabWidth		  =150;
}

CCJFlatTabCtrl::~CCJFlatTabCtrl()
{
	_delete(m_pLeftButton);
	_delete(m_pRightButton);
	_delete(m_pHomeButton);
	_delete(m_pEndButton);

	// free HENHMETAFILE handles.
	if(m_hLeftArrow) {
		::DeleteEnhMetaFile(m_hLeftArrow);
	}
	if(m_hLeftArrowDisabled) {
		::DeleteEnhMetaFile(m_hLeftArrowDisabled);
	}
	if(m_hRightArrow) {
		::DeleteEnhMetaFile(m_hRightArrow);
	}
	if(m_hRightArrowDisabled) {
		::DeleteEnhMetaFile(m_hRightArrowDisabled);
	}
	if(m_hHomeArrow) {
		::DeleteEnhMetaFile(m_hHomeArrow);
	}
	if(m_hHomeArrowDisabled) {
		::DeleteEnhMetaFile(m_hHomeArrowDisabled);
	}
	if(m_hEndArrow) {
		::DeleteEnhMetaFile(m_hEndArrow);
	}
	if(m_hEndArrowDisabled) {
		::DeleteEnhMetaFile(m_hEndArrowDisabled);
	}
	while(!m_TabList.IsEmpty()) {
		CString* pItem = m_TabList.RemoveTail();
		_delete(pItem);
	}
	while(!m_TabTipList.IsEmpty()) {
		CString* pItem = m_TabTipList.RemoveTail();
		_delete(pItem);
	}

	// fix potential resource leak - KStowell - 10-21-99
	m_Font.DeleteObject();
	m_BoldFont.DeleteObject();
}

void CCJFlatTabCtrl::CreateTabFont(const int iTabHeight)
{
	int iHeight = -(iTabHeight - 6);

	m_Font.DeleteObject();
	m_Font.CreateFont(iHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
		OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, FF_SWISS, NULL);

	m_BoldFont.DeleteObject();
	m_BoldFont.CreateFont(iHeight, 0, 0, 0, FW_EXTRABOLD, FALSE, FALSE, FALSE, ANSI_CHARSET,
		OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, PROOF_QUALITY, FF_SWISS, NULL);
}

int CCJFlatTabCtrl::GetTabWidth(const int nItem) const
{
	CDC dc;
	dc.CreateCompatibleDC(NULL);
	CFont* pOldFont = dc.SelectObject((CFont*)&m_BoldFont);
	CSize size(dc.GetTextExtent(*m_TabList.GetAt(m_TabList.FindIndex(nItem))));
	if(size.cx>m_nMaxTabWidth)
		size.cx=m_nMaxTabWidth;
	// fix potential resource leak - KStowell - 10-20-99.
	dc.SelectObject(pOldFont);
	dc.DeleteDC();
	
	return size.cx + m_iClientHeight + (m_iClientHeight / 2);
}

int CCJFlatTabCtrl::GetTotalTabWidth(void) const
{
	int iWidth = 0;
	for (int i = 0; i < m_TabList.GetCount(); i++)
	{
		iWidth += GetTabWidth(i);
		if (i != m_TabList.GetCount()-1) {
			iWidth -= (m_iClientHeight / 2);
		}
	}
	return iWidth + 2;
}

int CCJFlatTabCtrl::GetTotalArrowWidth(void) const
{
	int iWidth = 1;
	if (m_bHasArrows)
		iWidth += 32;
	if (m_bHasHomeEnd)
		iWidth += 32;
	return iWidth;
}

void CCJFlatTabCtrl::InvalidateTabs(void)
{
	if (GetSafeHwnd())
	{
		// invalidate the visible tab area
		// to minimise flicker - don't erase the background
		CRect rcTabs;

		rcTabs.left = GetTotalArrowWidth();
		rcTabs.top = 0;
		rcTabs.right = rcTabs.left + (m_iTotalTabWidth - m_iXOffset);
		rcTabs.bottom = m_iClientHeight;
		InvalidateRect(&rcTabs, false);
		// invalidate the blank area to the right of the tabs
		if (rcTabs.right < m_iClientWidth)
		{
			rcTabs.left = rcTabs.right;
			rcTabs.right = m_iClientWidth;
			InvalidateRect(&rcTabs, true);
		}
	}
}

void CCJFlatTabCtrl::EnableButtons(void)
{
	if (m_bHasArrows)  //TCC: ADDED to handle situations where ther are no arrows
	{
		m_pLeftButton->EnableWindow(m_iXOffset);
		if (m_iClientWidth + m_iXOffset < m_iTotalTabWidth + GetTotalArrowWidth() + 1)
			m_pRightButton->EnableWindow(true);
		else
			m_pRightButton->EnableWindow(false);
		if (m_bHasHomeEnd)
		{
			m_pHomeButton->EnableWindow(m_iXOffset);
			m_pEndButton->EnableWindow(m_pRightButton->IsWindowEnabled());
		}
	}
}

int CCJFlatTabCtrl::DrawTab(CDC &dc, const int x, const int y, const int iHeight, bool bSelected, bool bBottom, LPCTSTR lpszTab) const
{
	CFont* pOldFont = dc.SelectObject((CFont*)&m_BoldFont);
	CSize size(dc.GetTextExtent(lpszTab));	
	if(size.cx>m_nMaxTabWidth)
	{
		size.cx=m_nMaxTabWidth;
	}
	CRect rcText;
	rcText.left = iHeight + x - 4;
	rcText.top = 2 + y;
	rcText.right = size.cx + iHeight + x - 4;
	rcText.bottom = size.cy + 2 + y;
	
	int iHalf = iHeight / 2;
	int iWidth = iHeight + iHalf;
	
	COLORREF crBack;
	COLORREF crFore;
	if (bSelected) {
		crBack = GetSysColor(COLOR_WINDOW);
		crFore = GetSysColor(COLOR_WINDOWTEXT);
	} else {
		crBack = GetSysColor(COLOR_3DFACE);
		crFore = GetSysColor(COLOR_BTNTEXT);
	}

	CPen penOutline(PS_SOLID, 1, GetSysColor(COLOR_BTNTEXT));
	CPen penShadow(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
	CBrush brush;
	brush.CreateSolidBrush(crBack);
	
	POINT points[4];
	points[0].x = x;                     points[0].y = iHeight + y - 1;
	points[1].x = iHalf + x;             points[1].y = y;
	points[2].x = size.cx + iHeight + x; points[2].y = y;
	points[3].x = size.cx + iWidth + x;  points[3].y = iHeight + y - 1;
	
	if (bBottom) {
		// swap vertical coordinates
		points[0].y = points[1].y;
		points[2].y = points[3].y;
		points[1].y = points[2].y;
		points[3].y = points[0].y;
	}

	CPen* pOldPen = dc.SelectObject(&penOutline);
	dc.SetBkColor(crBack);
	CBrush* pOldBrush = dc.SelectObject(&brush);
	dc.Polygon(points, 4);
	dc.SelectObject(bSelected ? (CFont*)&m_BoldFont : (CFont*)&m_Font);
	dc.SetTextColor(crFore);
	//dc.DrawText(lpszTab, rcText, DT_CENTER);	
	dc.DrawText(lpszTab, rcText, DT_LEFT);	
	dc.SelectObject(&penShadow);
	
	if (bSelected) {
		dc.MoveTo(iHalf + x + 1, points[1].y);
		dc.LineTo(size.cx + iHeight + x, points[1].y);
		CPen penBack(PS_SOLID, 1, crBack);
		dc.SelectObject(&penBack);
		dc.MoveTo(x + 1, points[0].y);
		dc.LineTo(size.cx + iWidth + x, points[0].y);
	} else {
		dc.MoveTo(iHalf + x + 1, points[1].y);
		dc.LineTo(size.cx + iHeight + x - 1, points[1].y);
		dc.LineTo(size.cx + iWidth + x - 1, points[0].y);
	}

	// fix potential resource leak - KStowell - 10-21-99
	dc.SelectObject(pOldFont);
	dc.SelectObject(pOldPen);
	dc.SelectObject(pOldBrush);
	penOutline.DeleteObject();
	penShadow.DeleteObject();
	brush.DeleteObject();

	return size.cx + iWidth;
}

BOOL CCJFlatTabCtrl::InsertItem(const int nItem, LPCTSTR lpszItem,CWnd* pWnd)
{
	if (nItem < 0 || nItem > m_TabList.GetCount())
		return -1;

	// add tab to our list
	CString* pItem = new CString;
	ASSERT(pItem);

	*pItem = lpszItem;
	m_TabList.AddTail(pItem);
	m_TabWndList.AddTail(pWnd);
    // PGB - Add the tabs tip text to the list

	CString* pTipItem = new CString;
	ASSERT(pTipItem);

	*pTipItem = lpszItem;
	m_TabTipList.AddTail(pTipItem);

	if (m_TabList.GetCount() == 1)
		m_iCurSel = 0;

   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item
	
    EnableButtons();
	InvalidateTabs();

	return nItem;
}

BOOL CCJFlatTabCtrl::DeleteItem(int nItem)
{
	if (nItem < 0 || nItem >= m_TabList.GetCount())
		return FALSE;
	
	POSITION pos = m_TabList.FindIndex(nItem);
	ASSERT(pos);	
	CString* pItem = m_TabList.GetAt(pos);
	m_TabList.RemoveAt(pos);
	_delete(pItem);
	//Delete Wnd;
	pos=m_TabWndList.FindIndex(nItem);
	ASSERT(pos);
	m_TabWndList.RemoveAt(pos);	
	//Delet Tip
	pos=m_TabTipList.FindIndex(nItem);
	ASSERT(pos);
	pItem = m_TabTipList.GetAt(pos);
	m_TabTipList.RemoveAt(pos);	
	_delete(pItem);	

    // PGB - Stole the following code from the OnLButtonDown() function
    // in order to cause the tab control to show the newly selected item
    // after deleting an item from the list

	// warn parent that the selection is about to change
	int id = GetDlgCtrlID();
	NMHDR hdr;
	hdr.hwndFrom = m_hWnd;
	hdr.idFrom = id;
	hdr.code = TCN_SELCHANGING;
	if (GetParent()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0)
	{
	    if (m_iCurSel >= m_TabList.GetCount())
        {
            SetCurSel(m_TabList.GetCount() - 1); // PGB - Reset the currently selected tab to the last one in the list
			InvalidateTabs();
        }

		// notify parent that the selection has changed
		hdr.hwndFrom = m_hWnd;
		hdr.idFrom = id;
		hdr.code = TCN_SELCHANGE;
		GetParent()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr);
	}

   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have deleted an item

    EnableButtons();
	InvalidateTabs();

	return TRUE;
}

BOOL CCJFlatTabCtrl::DeleteAllItems()
{
	while(!m_TabList.IsEmpty()) {
		CString* pItem = m_TabList.RemoveTail();
		_delete(pItem);
	}

	while(!m_TabTipList.IsEmpty()) {
		CString* pItem = m_TabTipList.RemoveTail();
		_delete(pItem);
	}

	m_iCurSel = -1;                             // PGB - Reset the currently selected tab to -1 as we have no tabs in our list now.

   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have deleted all items

    EnableButtons();
	InvalidateTabs();

	return TRUE;
}

BOOL CCJFlatTabCtrl::GetItemRect(int nItem, LPRECT lpRect)
{
	if (nItem < 0 || nItem >= m_TabList.GetCount())
		return FALSE;
	
	int x = GetTotalArrowWidth();
	for (int i = 0; i < nItem; i++)
	{
		x += GetTabWidth(i);
		if (i != m_TabList.GetCount())
			x -= (m_iClientHeight / 2) + 2;
	}
	lpRect->left = x - m_iXOffset;
	lpRect->top = 0;
	lpRect->right = lpRect->left + GetTabWidth(nItem);
	lpRect->bottom = m_iClientHeight;
	return TRUE;
}

int CCJFlatTabCtrl::HitTest(TCHITTESTINFO *pHitTestInfo) const
{
	// ignore hits on the buttons
	int iHitX = pHitTestInfo->pt.x;
	if (iHitX < GetTotalArrowWidth())
		return -1;
	
	// check if any tabs were hit
	int x = GetTotalArrowWidth() - m_iXOffset;
	for (int i = 0; i < m_TabList.GetCount(); i++)
	{
		int iTabWidth = GetTabWidth(i);
		if (i != m_TabList.GetCount())
			iTabWidth -= (m_iClientHeight / 2) + 2;
		if ((x <= iHitX) && (iHitX <= x + iTabWidth))
			return i;
		x += iTabWidth;
	}
	
	// hit point is right of rightmost tab
	return -1;
}

int CCJFlatTabCtrl::SetCurSel(int nItem)
{
	if (nItem < 0 || nItem >= m_TabList.GetCount())
		return -1;
	
	int iPrevSel = m_iCurSel;
	m_iCurSel = nItem;
	
	// test if we need to center on the selected tab
	CRect rcItem;
	if (GetItemRect(nItem, &rcItem))
	{
		// test if the tab is off on the left
		int iTotalArrowWidth = GetTotalArrowWidth();
		rcItem.left -= iTotalArrowWidth;
		if (rcItem.left < 0)
			m_iXOffset += rcItem.left;
		else
		{
			// test if the tab is off on the right
			rcItem.right -= iTotalArrowWidth;
			int iTabAreaWidth = m_iClientWidth - iTotalArrowWidth;
			if (rcItem.right > iTabAreaWidth)
				m_iXOffset += (rcItem.right - iTabAreaWidth);
		}
	}
	
	EnableButtons();
	InvalidateTabs();

	return iPrevSel;
}

BEGIN_MESSAGE_MAP(CCJFlatTabCtrl, CWnd)
	//{{AFX_MSG_MAP(CCJFlatTabCtrl)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_SIZE()
	ON_WM_CREATE()
	ON_BN_CLICKED(IDC_LEFTBUTTON, OnLeftArrow)
	ON_BN_CLICKED(IDC_RIGHTBUTTON, OnRightArrow)
	ON_BN_CLICKED(IDC_HOMEBUTTON, OnHomeArrow)
	ON_BN_CLICKED(IDC_ENDBUTTON, OnEndArrow)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CCJFlatTabCtrl message handlers

BOOL CCJFlatTabCtrl::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!CWnd::PreCreateWindow(cs))
		return FALSE;
	
	cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS,
            ::LoadCursor(NULL, IDC_ARROW), NULL, NULL);
	
	return TRUE;
}

BOOL CCJFlatTabCtrl::PreTranslateMessage(MSG* pMsg)
{
	ASSERT_VALID(this);

    if (::IsWindow(m_ToolTip.m_hWnd))
                                                                                                                                                                                                                                                                                                                                                                                                                           {
	    if(pMsg->message == WM_MOUSEMOVE && pMsg->hwnd == m_hWnd)
        {
		    CPoint Point(LOWORD(pMsg->lParam), HIWORD(pMsg->lParam));

	        TCHITTESTINFO test;
	        test.pt = Point;
	        int iTab = HitTest(&test);

            if (iTab >= 0)
            {
                CString strText;

                strText = (LPCTSTR) *m_TabTipList.GetAt(m_TabTipList.FindIndex(iTab));

                TRACE2("Mouse over tab %d : %s\n", iTab, strText);

			    m_ToolTip.UpdateTipText(strText, this);
			    m_ToolTip.RelayEvent(pMsg);
            } else  {
				// VC5 compatibility - Kirk Stowell - 10-21-99
				::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L);
            }
        }
        else {
			// VC5 compatibility - Kirk Stowell - 10-21-99
			::SendMessage(m_ToolTip.m_hWnd, TTM_POP, 0, 0L);
        }
    }

	return CWnd::PreTranslateMessage(pMsg);
}

void CCJFlatTabCtrl::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	CBrush brush;
	brush.CreateSysColorBrush(COLOR_3DFACE);
	CPen penBlack(PS_SOLID, 1, RGB(0,0,0));
	
	// draw the black line along the left of the control
	CPen* pOldPen = dc.SelectObject(&penBlack);
	dc.MoveTo(0, 0);
	dc.LineTo(0, m_iClientHeight);
	
	// draw the black line along the top/bottom of the control
	int iTotalArrowWidth = GetTotalArrowWidth();
	int iEndOfTab = iTotalArrowWidth + m_iTotalTabWidth - m_iXOffset;
	dc.MoveTo(iEndOfTab, m_bTabsOnBottom ? 0 : m_iClientHeight - 1);
	dc.LineTo(m_iClientWidth, m_bTabsOnBottom ? 0 : m_iClientHeight - 1);
	
	// fill the empty background area
	int iFillHeight = m_iClientHeight - (m_bTabsOnBottom ? 0 : 1);
	CRect rcBack(iEndOfTab, m_bTabsOnBottom ? 1 : 0, m_iClientWidth, iFillHeight);
	dc.FillRect(rcBack, &brush);
	
	// create a bitmap of all the tabs
	CDC dcMem;
	dcMem.CreateCompatibleDC(NULL);
	CBitmap bitmap;
	bitmap.CreateCompatibleBitmap(&dc, m_iTotalTabWidth, m_iClientHeight);
	CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap);
	CBrush* pOldBrush = dcMem.SelectObject(&brush);
	CRect rc(0, m_bTabsOnBottom ? 1 : 0, m_iTotalTabWidth, iFillHeight);
	dcMem.FillRect(rc, &brush);
	
	int iOverlap = (m_iClientHeight / 2) + 2;
	int x = 0;
	int iSelX = 0;
	for (int i = 0; i < m_TabList.GetCount(); i++)
	{
		if (i != m_iCurSel)
			x += DrawTab(dcMem, x, 0, m_iClientHeight, false, m_bTabsOnBottom, *m_TabList.GetAt(m_TabList.FindIndex(i))) - iOverlap;
		else
		{
			iSelX = x;
			x += GetTabWidth(i) - iOverlap;
		}
	}
    if (m_iCurSel >= 0)
    	DrawTab(dcMem, iSelX, 0, m_iClientHeight, true, m_bTabsOnBottom, *m_TabList.GetAt(m_TabList.FindIndex(m_iCurSel)));
	
	// blit the bitmap onto the control
	dc.BitBlt(iTotalArrowWidth, 0, m_iClientWidth - iTotalArrowWidth, m_iClientHeight, &dcMem, m_iXOffset, 0, SRCCOPY);

	// fix potential resource leak - KStowell - 10-21-99.
	dcMem.SelectObject(pOldPen);
	dcMem.SelectObject(pOldBitmap);
	dcMem.SelectObject(pOldBrush);
	dcMem.DeleteDC();
	brush.DeleteObject();
	penBlack.DeleteObject();
	bitmap.DeleteObject();
}

void CCJFlatTabCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
	// TODO: Add your message handler code here and/or call default
	TCHITTESTINFO test;
	test.pt = point;
	int iTab = HitTest(&test);
	if ((iTab != -1) && (iTab != m_iCurSel))
	{
		// warn parent that the selection is about to change
		int id = GetDlgCtrlID();
		NMHDR hdr;
		hdr.hwndFrom = m_hWnd;
		hdr.idFrom = id;
		hdr.code = TCN_SELCHANGING;
		if (AfxGetMainWnd()->SendMessage(WM_NOTIFY, id, (LPARAM)&hdr) == 0)
		{
			// parent has given permission for the selection to change
			SetCurSel(iTab);
			InvalidateTabs();
			
			// notify parent that the selection has changed
			hdr.hwndFrom = m_hWnd;
			hdr.idFrom = id;
			//????һ?и?Ϊ????һ??
			//hdr.idFrom=(int)GetItemWnd(iTab);
			hdr.code = TCN_SELCHANGE;
			::SendMessage(AfxGetMainWnd()->m_hWnd,WM_NOTIFY, id, (LPARAM)&hdr);
		}
	}
	
	CWnd::OnLButtonDown(nFlags, point);
}

void CCJFlatTabCtrl::OnSize(UINT nType, int cx, int cy) 
{
	CWnd::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here
	if (m_bHasHomeEnd) {
		m_pHomeButton->MoveWindow(1, 0, 16, cy);
		m_pLeftButton->MoveWindow(17, 0, 16, cy);
		m_pRightButton->MoveWindow(33, 0, 16, cy);
		m_pEndButton->MoveWindow(49, 0, 16, cy);
	} else {
		if (m_bHasArrows) { //TCC: ADDED to handle situations where ther are no arrows
			m_pLeftButton->MoveWindow(1, 0, 16, cy);
			m_pRightButton->MoveWindow(17, 0, 16, cy);
		}
	}
	
	m_iClientWidth  = cx;
	m_iClientHeight = cy;

	CreateTabFont(cy);
	
	m_iTotalTabWidth = GetTotalTabWidth();
	EnableButtons();
}

int CCJFlatTabCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	// TODO: Add your specialized creation code here
	if (lpCreateStruct->style & FTS_BOTTOM) {
		m_bTabsOnBottom = true;
	}

    if (lpCreateStruct->style & TCS_TOOLTIPS) {
	    m_ToolTip.Create(this);
	    m_ToolTip.Activate(TRUE);
	    m_ToolTip.AddTool(this, _T("Flat Tab Tool Tip"));
        m_ToolTip.SendMessage(TTM_SETMAXTIPWIDTH, 0, SHRT_MAX); // Allow multiline tooltips
    }

	if (lpCreateStruct->style & FTS_HASARROWS)
	{
		m_bHasArrows = true;
		POINT points[4];
		points[0].x = 100; points[0].y = 0;
		points[1].x = 0;   points[1].y = 50;
		points[2].x = 100; points[2].y = 100;
		points[3].x = 100; points[3].y = 0;

		////////////////////////////////////////////////////////////////////////////////
		// This portion of the code was re-written to help eliminate a nasty resource
		// leak. It would seem that the CMetaFileDC was not freeing the GDI resources
		// completely whenever CMetaFileDC::CloseEnhanced() was called. This may be due 
		// to a BUG in MFC or perhaps this class, in any event, this seems to resolve 
		// the issue. - KStowell - 10-23-99.
		////////////////////////////////////////////////////////////////////////////////
		HPEN   hPen			  = ::CreatePen( PS_SOLID, 1, RGB( 0,0,0 ));
		HPEN   hPenDisabled   = ::CreatePen( PS_SOLID, 1, RGB( 128,128,128 ));
		HBRUSH hBrush		  = ::CreateSolidBrush( RGB( 0,0,0 ));
		HBRUSH hBrushDisabled = ::CreateSolidBrush( RGB( 128,128,128 ));;
		
		// create left arrow metafile
		HDC hMetaDC	     = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
		HPEN hOldPen     = (HPEN)::SelectObject( hMetaDC, hPen );
		HBRUSH hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrush );
		::Polygon( hMetaDC, points, 4 );
		::SelectObject( hMetaDC, hOldPen );
		::SelectObject( hMetaDC, hOldBrush );
		m_hLeftArrow = ::CloseEnhMetaFile( hMetaDC );

		// create disabled left arrow metafile
		hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
		hOldPen   = (HPEN)::SelectObject( hMetaDC, hPenDisabled );
		hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrushDisabled );
		::Polygon( hMetaDC, points, 4 );
		::SelectObject( hMetaDC, hOldPen );
		::SelectObject( hMetaDC, hOldBrush );
		m_hLeftArrowDisabled = ::CloseEnhMetaFile( hMetaDC );

		points[0].x = 0;   points[0].y = 0;
		points[1].x = 100; points[1].y = 50;
		points[2].x = 0;   points[2].y = 100;
		points[3].x = 0;   points[3].y = 0;
		
		// create right arrow metafile
		hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
		hOldPen   = (HPEN)::SelectObject( hMetaDC, hPen );
		hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrush );
		::Polygon( hMetaDC, points, 4 );
		::SelectObject( hMetaDC, hOldPen );
		::SelectObject( hMetaDC, hOldBrush );
		m_hRightArrow = ::CloseEnhMetaFile( hMetaDC );
		
		// create disabled right arrow metafile
		hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
		hOldPen   = (HPEN)::SelectObject( hMetaDC, hPenDisabled );
		hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrushDisabled );
		::Polygon( hMetaDC, points, 4 );
		::SelectObject( hMetaDC, hOldPen );
		::SelectObject( hMetaDC, hOldBrush );
		m_hRightArrowDisabled = ::CloseEnhMetaFile( hMetaDC );
		
		CRect rcButton(0, 0, 16, 16);
		m_pLeftButton = new CCJMetaFileButton;
		m_pLeftButton->Create(NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, rcButton, this, IDC_LEFTBUTTON);
		m_pLeftButton->SetMetaFiles(m_hLeftArrow, 0, 0, m_hLeftArrowDisabled);
		
		m_pRightButton = new CCJMetaFileButton;
		m_pRightButton->Create(NULL, WS_CHILD | WS_VISIBLE | WS_DISABLED | BS_OWNERDRAW, rcButton, this, IDC_RIGHTBUTTON);
		m_pRightButton->SetMetaFiles(m_hRightArrow, 0, 0, m_hRightArrowDisabled);
		
		if (lpCreateStruct->style & FTS_HASHOMEEND)
		{
			m_bHasHomeEnd = true;

            // Setup the Home arrow button
       		points[0].x = 100;   points[0].y = 0;
		    points[1].x = 0;     points[1].y = 50;
		    points[2].x = 100;   points[2].y = 100;
		    points[3].x = 100;   points[3].y = 0;

			// create right arrow metafile
			HDC hMetaDC	     = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
			HPEN hOldPen     = (HPEN)::SelectObject( hMetaDC, hPen );
			HBRUSH hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrush );
			::Polygon( hMetaDC, points, 4 );
			::MoveToEx( hMetaDC, 0, 0, NULL );
			::LineTo( hMetaDC, 0, 100 );
			::SelectObject( hMetaDC, hOldPen );
			::SelectObject( hMetaDC, hOldBrush );
			m_hHomeArrow = ::CloseEnhMetaFile( hMetaDC );
		    
			// create disabled right arrow metafile
			hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
			hOldPen   = (HPEN)::SelectObject( hMetaDC, hPenDisabled );
			hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrushDisabled );
			::Polygon( hMetaDC, points, 4 );
			::MoveToEx( hMetaDC, 0, 0, NULL );
			::LineTo( hMetaDC, 0, 100 );
			::SelectObject( hMetaDC, hOldPen );
			::SelectObject( hMetaDC, hOldBrush );
			m_hHomeArrowDisabled = ::CloseEnhMetaFile( hMetaDC );

			m_pHomeButton = new CCJMetaFileButton;
			m_pHomeButton->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, rcButton, this, IDC_HOMEBUTTON);
			m_pHomeButton->SetMetaFiles(m_hHomeArrow, 0, 0, m_hHomeArrowDisabled);

            // Setup the End arrow button
       		points[0].x = 0;   points[0].y = 0;
		    points[1].x = 100; points[1].y = 50;
		    points[2].x = 0;   points[2].y = 100;
		    points[3].x = 0;   points[3].y = 0;

			// create right arrow metafile
			hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
			hOldPen   = (HPEN)::SelectObject( hMetaDC, hPen );
			hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrush );
			::Polygon( hMetaDC, points, 4 );
			::MoveToEx( hMetaDC, 100, 0, NULL );
			::LineTo( hMetaDC, 100, 100 );
			::SelectObject( hMetaDC, hOldPen );
			::SelectObject( hMetaDC, hOldBrush );
			m_hEndArrow = ::CloseEnhMetaFile( hMetaDC );
		    
			// create disabled right arrow metafile
			hMetaDC	  = ::CreateEnhMetaFile( NULL, NULL, NULL, NULL );
			hOldPen   = (HPEN)::SelectObject( hMetaDC, hPenDisabled );
			hOldBrush = (HBRUSH)::SelectObject( hMetaDC, hBrushDisabled );
			::Polygon( hMetaDC, points, 4 );
			::MoveToEx( hMetaDC, 100, 0, NULL );
			::LineTo( hMetaDC, 100, 100 );
			::SelectObject( hMetaDC, hOldPen );
			::SelectObject( hMetaDC, hOldBrush );
			m_hEndArrowDisabled = ::CloseEnhMetaFile( hMetaDC );

			m_pEndButton = new CCJMetaFileButton;
			m_pEndButton->Create(NULL, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, rcButton, this, IDC_ENDBUTTON);
			m_pEndButton->SetMetaFiles(m_hEndArrow, 0, 0, m_hEndArrowDisabled);
			
			m_pHomeButton->MoveWindow(1, 0, 16, 16);
			m_pLeftButton->MoveWindow(17, 0, 16, 16);
			m_pRightButton->MoveWindow(33, 0, 16, 16);
			m_pEndButton->MoveWindow(49, 0, 16, 16);

		} else {
			m_pLeftButton->MoveWindow(1, 0, 16, 16);
			m_pRightButton->MoveWindow(17, 0, 16, 16);
		}

		// fix potential resource leak - KStowell - 10-23-99
		::DeleteObject(hPen);
		::DeleteObject(hPenDisabled);
		::DeleteObject(hBrush);
		::DeleteObject(hBrushDisabled);
	}

	return 0;
}

void CCJFlatTabCtrl::OnLeftArrow() 
{
    // Move the tabs right, ensuring that we move right by one
    // whole tab each time ala Microsoft Access

	CPoint Point(GetTotalArrowWidth()+1, 1);

	TCHITTESTINFO test;
	test.pt = Point;
	int iTab = HitTest(&test);

    if (iTab != -1)
    {
        m_iXOffset = 0;
        for (int iLoop = 0; iLoop <= iTab - 1; iLoop++)
        {
            RECT rect;
            if (GetItemRect(iLoop, &rect))
            {
                m_iXOffset += rect.left;
            }
        }
        m_iXOffset -= GetTotalArrowWidth();

	    EnableButtons();
	    InvalidateTabs();
    }
}

void CCJFlatTabCtrl::OnRightArrow() 
{
    // Move the tabs left, ensuring that we move left by one
    // whole tab each time ala Microsoft Access

	CPoint Point(GetTotalArrowWidth()+1, 1);

	TCHITTESTINFO test;
	test.pt = Point;
	int iTab = HitTest(&test);

    if (iTab != -1)
    {
        m_iXOffset = 0;
        for (int iLoop = 0; iLoop <= iTab + 1; iLoop++)
        {
            RECT rect;
            if (GetItemRect(iLoop, &rect))
            {
                m_iXOffset += rect.left;
            }
        }
        m_iXOffset -= GetTotalArrowWidth();

	    EnableButtons();
	    InvalidateTabs();
    }
}

void CCJFlatTabCtrl::OnHomeArrow() 
{
	// TODO: Add your control notification handler code here
	m_iXOffset = 0;
	EnableButtons();
	InvalidateTabs();
}

void CCJFlatTabCtrl::OnEndArrow() 
{
	// TODO: Add your control notification handler code here
	m_iXOffset = m_iTotalTabWidth + GetTotalArrowWidth() - m_iClientWidth + 1;
	EnableButtons();
	InvalidateTabs();
}

void CCJFlatTabCtrl::SetTipText(int nItem, LPCTSTR lpszTabTip)
{
    if (nItem < m_TabList.GetCount())
    {
	    for (int i = 0; i < m_TabList.GetCount(); i++)
	    {
            if (i == nItem)
            {
                CString *pTipItem = m_TabTipList.GetAt(m_TabTipList.FindIndex(nItem));

            	*pTipItem = lpszTabTip;

                break;
            }
	    }
    }

    return;
}

CString CCJFlatTabCtrl::GetTipText(int nItem)
{
    CString strText;

    if (nItem < m_TabList.GetCount())
    {
	    for (int i = 0; i < m_TabList.GetCount(); i++)
	    {
            if (i == nItem)
            {
                strText = *m_TabTipList.GetAt(m_TabTipList.FindIndex(nItem));
                break;
            }
	    }
    }

    return strText;
}

void CCJFlatTabCtrl::Home()
{
    OnHomeArrow();
}

void CCJFlatTabCtrl::SetMaxTabWidth(int nMaxTabWidth)
{
	m_nMaxTabWidth=nMaxTabWidth;
}

BOOL CCJFlatTabCtrl::SetItemText(const int nItem, LPCTSTR lpszItem)
{
	if (nItem < 0 || nItem > m_TabList.GetCount())
		return FALSE;	
	POSITION pos = m_TabList.FindIndex(nItem);
	ASSERT(pos);	
	CString* pOldItem = m_TabList.GetAt(pos);	
	CString* pItem = new CString;
	ASSERT(pItem);
	*pItem = lpszItem;
	m_TabList.SetAt(pos,pItem);
	_delete(pOldItem);
    // PGB - Add the tabs tip text to the list
	SetTipText(nItem,lpszItem);   
   	m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item
	EnableButtons();
	InvalidateTabs();
	return TRUE;
}

CWnd* CCJFlatTabCtrl::GetItemWnd(int nItem)
{
	CWnd* pWnd;	
    if (nItem>-1 && nItem < m_TabList.GetCount())
    {
	     pWnd = (CWnd*)m_TabWndList.GetAt(m_TabWndList.FindIndex(nItem));
		 return pWnd;
    }
	else
	{
		return NULL;
	}    
}


BOOL CCJFlatTabCtrl::SetItemText(CWnd *pWnd, LPCTSTR lpszItem)
{
	if(pWnd==NULL)
		return FALSE;
	int nIndex=0;
	for (POSITION pos=m_TabWndList.GetHeadPosition(); pos; m_TabWndList.GetNext(pos))
	{
		CWnd *pMember=m_TabWndList.GetAt(pos);
		if (pMember==pWnd)
		{
            SetItemText(nIndex, lpszItem);
			SetTipText(nIndex,lpszItem);
			// PGB - Add the tabs tip text to the list	
   			//m_iTotalTabWidth = GetTotalTabWidth();      // PGB - Recalculate the total tab width now we have added a new item
			//EnableButtons();
			//InvalidateTabs();
			return TRUE;
		}
        nIndex++;
    }
	return FALSE;
}

BOOL CCJFlatTabCtrl::SetItemSel(CWnd *pWnd)
{
	if(pWnd==NULL)
		return FALSE;
	int nIndex=0;
	for (POSITION pos=m_TabWndList.GetHeadPosition(); pos; m_TabWndList.GetNext(pos))
	{
		CWnd *pMember=m_TabWndList.GetAt(pos);
		if (pMember==pWnd)
		{
            SetCurSel(nIndex);
			return TRUE;
		}
        nIndex++;
    }
	return FALSE;
}